package com.caipos.actor.ruleChain;

import com.caipos.actor.ActorSystemContext;
import com.caipos.actor.common.actors.KbActorCtx;
import com.caipos.actor.common.data.id.RuleNodeId;
import com.caipos.actor.common.data.id.TenantId;
import com.caipos.actor.common.data.rule.RuleNode;
import com.caipos.actor.common.msg.KbMsg;
import com.caipos.actor.shared.ComponentMsgProcessor;
import com.caipos.actor.api.KbNode;
import com.caipos.actor.api.KbNodeConfiguration;
import com.caipos.util.DebugModeUtil;
import com.caipos.util.RuleNodeException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RuleNodeActorMessageProcessor extends ComponentMsgProcessor<RuleNodeId> {

    private static final String UNKNOWN_NAME = "Unknown";
    private final String ruleChainName;
//    private final KbApiUsageReportClient apiUsageClient;
    private final DefaultKbContext defaultCtx;
    private RuleNode ruleNode;
    private KbNode kbNode;
//    private RuleNodeInfo info;

    RuleNodeActorMessageProcessor(TenantId tenantId, String ruleChainName,
                                  RuleNodeId ruleNodeId, ActorSystemContext systemContext, KbActorCtx selfActor) {
        super(systemContext, tenantId, ruleNodeId);
        this.ruleChainName = ruleChainName;
        this.ruleNode = systemContext.getRuleNodeService().findRuleNodeById(entityId.getId());
        this.defaultCtx = new DefaultKbContext(systemContext, ruleChainName, new RuleNodeCtx(tenantId, selfActor, ruleNode));
    }

    @Override
    public void start(KbActorCtx context) throws Exception {
        if (isMyNodePartition()) {
            log.debug("[{}][{}] Starting", tenantId, entityId);
            kbNode = initComponent(ruleNode);
        }
    }

    @Override
    public void onUpdate(KbActorCtx context) throws Exception {
        RuleNode newRuleNode = null;// systemContext.getRuleChainService().findRuleNodeById(tenantId, entityId);
        if (isMyNodePartition(newRuleNode)) {
//            boolean restartRequired = state != ComponentLifecycleState.ACTIVE ||
//                    !(ruleNode.getType().equals(newRuleNode.getType()) &&
//                            ruleNode.getConfiguration().equals(newRuleNode.getConfiguration()));
//            this.ruleNode = newRuleNode;
//            this.defaultCtx.updateSelf(newRuleNode);
//            if (restartRequired) {
//                if (kbNode != null) {
//                    kbNode.destroy();
//                }
//                try {
//                    start(context);
//                } catch (Exception e) {
//                    throw new KbRuleNodeUpdateException("Failed to update rule node", e);
//                }
//            }
        } else if (kbNode != null) {
            stop(null);
            kbNode = null;
        }
    }

    @Override
    protected RuleNodeException getInactiveException() {
        return new RuleNodeException("Rule Node is not active! Failed to initialize.", ruleChainName, ruleNode);
    }

    @Override
    public void stop(KbActorCtx context) {
        log.debug("[{}][{}] Stopping", tenantId, entityId);
        if (kbNode != null) {
            kbNode.destroy();
//            state = ComponentLifecycleState.SUSPENDED;
        }
    }


    @Override
    public String getComponentName() {
        return getName(ruleNode);
    }

    private String getName(RuleNode ruleNode) {
        return ruleNode != null ? ruleNode.getName() : UNKNOWN_NAME;
    }

    private KbNode initComponent(RuleNode ruleNode) throws Exception {
        KbNode kbNode = null;
        if (ruleNode != null) {
            System.err.println(ruleNode.getName()+",ruletype:"+ruleNode.getType());
            Class<?> componentClazz = Class.forName(ruleNode.getType());
            kbNode = (KbNode) (componentClazz.getDeclaredConstructor().newInstance());
            kbNode.init(defaultCtx, new KbNodeConfiguration(ruleNode.getConfiguration()));
        }
        return kbNode;
    }

    private boolean isMyNodePartition() {
        return isMyNodePartition(this.ruleNode);
    }

    private boolean isMyNodePartition(RuleNode ruleNode) {
        boolean result = ruleNode == null || !ruleNode.getSingletonMode()
//                || systemContext.getDiscoveryService().isMonolith()
                || defaultCtx.isLocalEntity(new RuleNodeId(ruleNode.getId()));
        if (!result) {
            log.trace("[{}][{}] Is not my node partition", tenantId, entityId);
        }
        return result;
    }

    public void onRuleToSelfMsg(RuleNodeToSelfMsg msg) throws Exception {
        checkComponentStateActive(msg.getMsg());
        KbMsg kbMsg = msg.getMsg();
        int ruleNodeCount = kbMsg.getAndIncrementRuleNodeCounter();
        var tenantProfileConfiguration = getTenantProfileConfiguration();
        int maxRuleNodeExecutionsPerMessage = tenantProfileConfiguration.getMaxRuleNodeExecsPerMessage();
        if (maxRuleNodeExecutionsPerMessage == 0 || ruleNodeCount < maxRuleNodeExecutionsPerMessage) {
//            apiUsageClient.report(tenantId, tbMsg.getCustomerId(), ApiUsageRecordKey.RE_EXEC_COUNT);
//            persistDebugInputIfAllowed(msg.getMsg(), "Self");
            try {
                kbNode.onMsg(defaultCtx, msg.getMsg());
            } catch (Exception e) {
                defaultCtx.tellFailure(msg.getMsg(), e);
            }
        } else {
            kbMsg.getCallback().onFailure(new RuleNodeException("Message is processed by more then " + maxRuleNodeExecutionsPerMessage + " rule nodes!", ruleChainName, ruleNode));
        }
    }
//    private void persistDebugInputIfAllowed(KbMsg msg, String fromNodeConnectionType) {
//        if (DebugModeUtil.isDebugAllAvailable(ruleNode)) {
//            systemContext.persistDebugInput(tenantId, entityId, msg, fromNodeConnectionType);
//        }
//    }

}
